package com.gitee.andy.util;

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

public class ProtostuffUtil {

	private static ConcurrentHashMap<Class<?>, Schema<?>> schemaCaches = new ConcurrentHashMap<>();

	private static Objenesis stdObjenesis = new ObjenesisStd();

	private static <T> Schema<T> get(Class<T> clazz) {
		Schema<?> schema = null;
		if ((schema = schemaCaches.get(clazz)) == null) {
			synchronized (ProtostuffUtil.class) {
				if ((schema = schemaCaches.get(clazz)) == null) {
					schema = RuntimeSchema.createFrom(clazz);
					schemaCaches.put(clazz, schema);
				}
			}
		}
		return (Schema<T>) schema;
	}

	public static <T> byte[] serialize(T obj) {
		Objects.requireNonNull(obj);
		LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		try {
			return ProtobufIOUtil.toByteArray(obj, get((Class<T>) obj.getClass()), buffer);
		} finally {
			buffer.clear();
			buffer = null;
		}
	}

	public static <T> T deserialize(Class<T> clazz, byte[] vals) {
		Objects.requireNonNull(vals);
		T message = stdObjenesis.newInstance(clazz);
		ProtobufIOUtil.mergeFrom(vals, message, get(clazz));
		return message;
	}
}
